namespace BM.Tools.WebControls.MForm.Navigation
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics.CodeAnalysis;
    using System.Linq;

    using BM.Tools.WebControls.MForm.Controls.Items;
    using BM.Tools.WebControls.MForm.Navigation.XPath;
    using BM.Tools.WebControls.MForm.Navigation.XPath.NodeTests;

    /// <summary>
    /// An implementation of the INavigatorRoot class for the OffspringItem type
    /// </summary>
    public class OffspringsNavigator : INavigatorRoot<OffspringItem>
    {        
        private readonly OffspringItem current;

        /// <summary>
        /// Initializes a new instance of the <see cref="OffspringsNavigator"/> class.
        /// </summary>
        /// <param name="current">
        /// The current item.
        /// </param>
        /// <param name="path">
        /// The path to be navigated to.
        /// </param>
        public OffspringsNavigator(OffspringItem current, Path path)
        {
            this.current = current;
            Path = path;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="OffspringsNavigator"/> class.
        /// </summary>
        /// <param name="current">
        /// The current item.
        /// </param>
        /// <param name="path">
        /// The path to be navigated to.
        /// </param>
        public OffspringsNavigator(OffspringItem current, string path)
        {
            this.current = current;
            Path = new Path(path);
        }

        /// <summary>
        /// Gets the current item.
        /// </summary>        
        public OffspringItem Current
        {
            get
            {
                return current;
            }
        }

        /// <summary>
        /// Gets the parent item.
        /// </summary>        
        public INavigator<OffspringItem> Parent
        {
            get
            {
                return null;
            }
        }

        /// <summary>
        /// Gets the path to navigate to.
        /// </summary>        
        public Path Path
        {
            get;
            private set;
        }

        /// <summary>
        /// gets the query for the item
        /// </summary>
        /// <param name="item">
        /// The item to be queried.
        /// </param>
        /// <returns>
        /// the navigator query
        /// </returns>
        public NavigatorQuery<OffspringItem> Query(OffspringItem item)
        {
            return new OffspringQuery(item);
        }

        /// <summary>
        /// selects the items with the specified path
        /// </summary>
        /// <returns>
        /// the enumeration of mathcing items.
        /// </returns>
        public IEnumerable<OffspringItem> Select()
        {
            INavigator<OffspringItem> currentNavigator = new StartingNavigator<OffspringItem>(this, Current.GetSelf());
            foreach (var locationStep in Path.LocationSteps)
            {
                currentNavigator = LocationStepNavigator<OffspringItem, LocationStep>.Create(
                        currentNavigator, locationStep);
            }

            return currentNavigator.Select();
        }

        /// <summary>
        /// selects the first matching item
        /// </summary>
        /// <returns>
        /// the first matching item
        /// </returns>
        public OffspringItem SelectSingle()
        {            
            return Select().FirstOrDefault();
        }

        /// <summary>
        /// returns the reverse path to the specified path
        /// </summary>
        /// <returns>
        /// the reverse path to the specified path
        /// </returns>
        /// <exception cref="InvalidOperationException">
        /// </exception>        
        [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "The operation is expensive.")]
        public string GetReversePath()
        {
            OffspringItem item = Current;
            var chunks = new List<string>();
            foreach (SingleLocationStep sls in Path.LocationSteps)
            {
                if (sls.NodeTest is DescendantOrSelfTest)
                {
                    throw new InvalidOperationException("Descendant or self cannot be used to get a reverse path");
                }

                if (sls.NodeTest is ParentTest)
                {
                    chunks.Add(item.Name);
                }
                else if (sls.NodeTest is ElementTest || sls.NodeTest is AttributeTest || sls.NodeTest is AllTest
                         || sls.NodeTest is AllInNamespaceTest || sls.NodeTest is AllWithLocalNameTest)
                {
                    chunks.Add("..");
                }

                item = item.SelectSingle(sls.NodeTest.Text);
            }

            chunks.Reverse();
            return String.Join("/", chunks.ToArray());
        }
    }
}